#include "Grid.h"

/****************************************************/
/***********************GridNode*********************/
/****************************************************/

GridNode::GridNode()					// default constructor
{
	nodeID = 0;
	xCoord = 0;
	yCoord = 0;
	clear = true;
	onPath = false;
	contains = '.';
	inventory = std::list<std::string>();
}

GridNode::GridNode(int nID, int x, int y, bool isC)	// create a node
{
	nodeID = nID;
	xCoord = x;
	yCoord = y;
	clear = isC;

	if(clear)
		contains = '.';
	else
		contains = 'B';

	onPath = false;

	inventory = std::list<std::string>();
}

GridNode::~GridNode()					// destroy a node
{
}

void GridNode::setID(int id)			// set the node id
{
	nodeID = id;
}	

void GridNode::setX(int x)				// set the x coordinate
{
	xCoord = x;
}

void GridNode::setY(int y)				// set the y coordinate
{
	yCoord = y;
}

int GridNode::getX()					// get the x coordinate of the node
{
	return xCoord;
}

int GridNode::getY()					// get the y coordinate of the node
{
	return yCoord;
}

Ogre::Vector3 GridNode::getPosition()	// return the position of this node
{
	return Ogre::Vector3(xCoord, 0, yCoord);
}

void GridNode::setClear()				// set the node as walkable
{
	clear = true;
	contains = '.';
}

void GridNode::setOccupied()			// set the node as occupied
{
	clear = false;
	contains = 'B';
}

bool GridNode::isClear()				// is the node walkable
{
	return clear;
}

void GridNode::setOnPath(bool choice)
{
	onPath = choice;
}

bool GridNode::isOnPath()
{
	return onPath;
}

bool GridNode::equals(GridNode* theOther) const
{
	return ( nodeID == theOther->getID() );
}

void GridNode::addItem( std::string item )
{
	inventory.push_back(item);
}

std::string GridNode::removeItem()
{
	if( inventory.empty() )
		return "";

	std::string theItem = inventory.front();
	inventory.pop_front();
	return theItem;
}

bool GridNode::hasInventory()
{
	return !(inventory.empty());
}

/****************************************************/
/************************GridRow*********************/
/****************************************************/
	//Already implemented in Grid.h



/****************************************************/
/************************Grid************************/
/****************************************************/

Grid::Grid(int numRows, int numCols, int nodeHeight, int nodeDiagonal)	// create a grid
{
	rows = numRows;
	cols = numCols;
	height = nodeHeight;
	diag = nodeDiagonal;
	data.resize(rows, GridRow(cols));

	std::vector<GridNode>::iterator it;
	std::vector<GridRow>::iterator it2;

	for(int i = 0, nID = 0; i < rows; i++ )
	{
		GridRow* row = new GridRow(cols);

		for(int j = 0; j < cols; j++, nID++ )
		{
			GridNode* gn = new GridNode( nID, j*height - (5*cols - 5), i*height - (5*rows - 5) );
			it = row->data.begin();
			row->data.insert( it + j, *gn );
			row->data.pop_back(); //Have to remove placeholder nodes that were created by calling resize()
		}
		
		it2 = this->data.begin();
		this->data.insert( it2 + i, *row );
		data.pop_back(); //Have to remove placeholder nodes that were created by calling resize()
	}
}

Grid::~Grid()							// destroy a grid
{
}

int* Grid::findGridCoords(GridNode* node)		    //Helper method for finding the coordinates of the node on the Grid
{
	int* location = new int[2];
	// x = row*height - (5*cols - 5) -> row = (x + 5*cols - 5) / height
	int c = (int)(((node->getX() + 5*cols - 5) / height));
	int r = (int)(((node->getY() + 5*rows - 5) / height));
	if( !(r < 0 || r > rows-1 || c < 0 || c > cols-1) )
	{
		location[0] = r;
		location[1] = c;
	}
		
	//bool breakOut = false;
	//for( unsigned int i = 0; i < data.size(); i++ )
	//{
	//	GridRow row = data.at(i);
	//	for(unsigned int j = 0; j < row.data.size(); j++)
	//	{
	//		if( node->equals( &row.data.at(j) ) )
	//		{
	//			location[0] = i;
	//			location[1] = j;
	//			breakOut = true;
	//			break;
	//		}
	//	}
	//	if( breakOut )
	//		break;
	//}

	return location;
}

GridNode* Grid::findNode( int x, int y )
{
	int r = (int)(((x + 5*cols - 5) / height));
	int c = (int)(((y + 5*rows - 5) / height));
	if( r < 0 || r > rows-1 || c < 0 || c > cols-1 )
		return NULL;
	
	return &(data.at(r).data.at(c));
	/*for( unsigned int i = 0; i < data.size(); i++ )
	{
		GridRow row = data.at(i);
		for( unsigned int j = 0; j < row.data.size(); j++ )
		{
			if( row.data.at(j).getX() == x && row.data.at(j).getY() == y )
			{
				return &(row.data.at(j));
			}
		}
	}*/

	return NULL;
}

GridNode* Grid::getNode(int r, int c)	// get the node specified.  Rows and columns go from 0 to n-1.  Returns NULL if out of bounds.
{										
	if( r < 0 || c < 0 || r >= rows || c >= cols )
		return NULL;

	return &( data.at(r).data.at(c) );
}

int Grid::getHeight()					// get the height and width of the nodes
{
	return height;
}

int Grid::getDiagonal()					// get the diagonal of the nodes
{
	return diag;
}

void Grid::setHeight(int h)				// set the height of the nodes
{
	height = h;
}

void Grid::setDiagonal(int d)			// set the Diagonal of the nodes
{
	diag = d;
}

GridNode* Grid::getNorthNode(GridNode* n)	// get adjacent nodes;
{
	GridNode* northNode;
	int* location = this->findGridCoords(n);

	if( *location < 0 || *location >= rows || *(location + 1) < 0 || *(location + 1) >= cols ) //GridNode doesn't exist
		northNode = NULL;
	else
	{
		int r = *location;
		int newR = r - 1;
		int c = *(location+1);
		northNode = getNode( newR, c );
	}

	delete[] location;
	return northNode;
}

GridNode* Grid::getSouthNode(GridNode* n)
{
	GridNode* southNode;
	int* location = this->findGridCoords(n);

	if( *location < 0 || *location >= rows || *(location + 1) < 0 || *(location + 1) >= cols ) //GridNode doesn't exist
		southNode = NULL;
	else
	{
		int r = *location;
		int newR = r + 1;
		int c = *(location+1);
		southNode = getNode( newR, c );
	}
	
	delete[] location;
	return southNode;
}

GridNode* Grid::getEastNode(GridNode* n)
{
	GridNode* eastNode;
	int* location = this->findGridCoords(n);

	if( *location < 0 || *location >= rows || *(location + 1) < 0 || *(location + 1) >= cols ) //GridNode doesn't exist
		eastNode = NULL;
	else
	{
		int r = *location;
		int c = *(location+1);
		int newC = c + 1;
		eastNode = getNode( r, newC );
	}
	
	delete[] location;
	return eastNode;
}

GridNode* Grid::getWestNode(GridNode* n)
{
	GridNode* westNode;
	int* location = this->findGridCoords(n);

	if( *location < 0 || *location >= rows || *(location + 1) < 0 || *(location + 1) >= cols ) //GridNode doesn't exist
		westNode = NULL;
	else
	{
		int r = *location;
		int c = *(location+1);
		int newC = c - 1;
		westNode = getNode( r, newC );
	}

	delete[] location;
	return westNode;
}

GridNode* Grid::getNENode(GridNode* n)
{
	GridNode* nENode;
	int* location = this->findGridCoords(n);

	if( *location < 0 || *location >= rows || *(location + 1) < 0 || *(location + 1) >= cols ) //GridNode doesn't exist
		nENode = NULL;
	else
	{
		int r = *location;
		int newR = r - 1;
		int c = *(location+1);
		int newC = c + 1;
		nENode = getNode( newR, newC );
	}

	delete[] location;
	return nENode;
}

GridNode* Grid::getNWNode(GridNode* n)
{
	GridNode* nWNode;
	int* location = this->findGridCoords(n);

	if( *location < 0 || *location >= rows || *(location + 1) < 0 || *(location + 1) >= cols ) //GridNode doesn't exist
		nWNode = NULL;
	else
	{
		int r = *location;
		int newR = r - 1;
		int c = *(location+1);
		int newC = c - 1;
		nWNode = getNode( newR, newC );
	}

	delete[] location;
	return nWNode;
}

GridNode* Grid::getSENode(GridNode* n)
{
	GridNode* sENode;
	int* location = this->findGridCoords(n);

	if( *location < 0 || *location >= rows || *(location + 1) < 0 || *(location + 1) >= cols ) //GridNode doesn't exist
		sENode = NULL;
	else
	{
		int r = *location;
		int newR = r + 1;
		int c = *(location+1);
		int newC = c + 1;
		sENode = getNode( newR, newC );
		//sENode = getNode( *location + 1, *(++location) + 1 );
	}

	delete[] location;
	return sENode;
}

GridNode* Grid::getSWNode(GridNode* n)
{
	GridNode* sWNode;
	int* location = this->findGridCoords(n);

	if( *location < 0 || *location >= rows || *(location + 1) < 0 || *(location + 1) >= cols ) //GridNode doesn't exist
		sWNode = NULL;
	else
	{
		int r = *location;
		int newR = r + 1;
		int c = *(location+1);
		int newC = c - 1;
		sWNode = getNode( newR, newC );
	}

	delete[] location;
	return sWNode;
}


int Grid::getDistance(GridNode* node1, GridNode* node2)  //get Manhattan distance between between two nodes
{
	if(node1->equals(node2))	//They're the same node... don't need to do any calculations :)
		return 0;

	int x1 = node1->getX();
	int x2 = node2->getX();
	int y1 = node1->getY();
	int y2 = node2->getY();

	int xDistance;
	int yDistance;

	xDistance = x1 - x2;
	yDistance = y1 - y2;

	if(xDistance < 0)
		xDistance = -xDistance;
	if(yDistance < 0)
		yDistance = -yDistance;

	return xDistance + yDistance;
}

/*
 * Assuming the nodes are adjacent.
 */
int Grid::getGcost(GridNode* node1, GridNode* node2)	// height or diagonal?
{
	int x1 = node1->getX();
	int x2 = node2->getX();
	int y1 = node1->getY();
	int y2 = node2->getY();

	if( x1 == x2 && y1 == y2 )
		return 0;
	else if( x1 == x2 || y1 == y2 )
		return 10;
	else
		return 14;
}

void Grid::printToFile()				// Print a grid to a file.  Good for debugging
{
	std::ofstream outFile;
	outFile.open("Grid.txt");
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			outFile << this->getNode(i, j)->contains << " ";
		}
		outFile << std::endl;
	}
	outFile.close();
}
